package strutil

import (
	"crypto/hmac"
	"crypto/sha256"
	"encoding/hex"
	"math/big"
	"strings"

	"github.com/gookit/goutil/byteutil"
)

// Md5 Generate a 32-bit md5 string
func Md5(src any) string { return string(Md5Bytes(src)) }

// MD5 Generate a 32-bit md5 string
func MD5(src any) string { return Md5(src) }

// GenMd5 Generate a 32-bit md5 string
func GenMd5(src any) string { return Md5(src) }

// Md5Simple md5 加密原始二进制的每个byte转为 base62，缩短长度(16)
func Md5Simple(src any) string {
	md5Bytes := byteutil.Md5Sum(src)
	b62str := new(strings.Builder)
	b62str.Grow(16)

	// 直接将每个 byte 转为 base62 数字然后拼接
	for _, b := range md5Bytes {
		b62str.WriteByte(Base62Chars[b%0x3E])
	}
	return b62str.String()
}

// Md5Base62 md5 加密原始二进制的转为 base62 字符串，缩短长度(21~22)
func Md5Base62(src any) string {
	md5Bytes := byteutil.Md5Sum(src)

	// Step 2: 将字节数组转为 big.Int
	number := new(big.Int)
	number.SetBytes(md5Bytes)

	// Step 3: 转换为 Base62 字符串
	base62 := new(strings.Builder)
	// base62.Grow(22)
	for number.Sign() > 0 {
		remainder := new(big.Int)
		number.DivMod(number, big.NewInt(62), remainder)
		base62.WriteByte(Base62Chars[remainder.Int64()])
	}

	// 反转字符串以得到正确的顺序
	runes := []rune(base62.String())
	for i, j := 0, len(runes)-1; i < j; i, j = i+1, j-1 {
		runes[i], runes[j] = runes[j], runes[i]
	}

	return string(runes)
}

// Md5Bytes Generate a 32-bit md5 bytes
func Md5Bytes(src any) []byte { return byteutil.Md5(src) }

// ShortMd5 Generate a 16-bit md5 string. remove the first 8 and last 8 bytes from 32-bit md5 string.
func ShortMd5(src any) string { return string(byteutil.ShortMd5(src)) }

//
// ----------------------- hash password -----------------------------
//

// HashPasswd for quick hash an input password string
func HashPasswd(pwd, key string) string {
	hm := hmac.New(sha256.New, []byte(key))
	hm.Write([]byte(pwd))

	return hex.EncodeToString(hm.Sum(nil))
}

// VerifyPasswd for quick verify input password is valid
//
// - pwdMAC from db or config, generated by EncryptPasswd()
func VerifyPasswd(pwdMAC, pwd, key string) bool {
	decBts, err := hex.DecodeString(pwdMAC)
	if err != nil {
		return false
	}

	hm := hmac.New(sha256.New, []byte(key))
	hm.Write([]byte(pwd))

	return hmac.Equal(decBts, hm.Sum(nil))
}
